package com.gitee.loyo.config;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.loyo.Request;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class ChargeRequestBodyAdvice extends RequestBodyAdviceAdapter {
    final ObjectMapper objectMapper;
    final ChargeNotifyProperties chargeNotifyProperties;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return methodParameter.getParameterType() == Request.class;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        return new ChargeHttpInputMessage(inputMessage, (Class<?>) ((ParameterizedTypeImpl) targetType).getActualTypeArguments()[0]);
    }

    public class ChargeHttpInputMessage implements HttpInputMessage {
        private HttpHeaders headers;
        private InputStream body;

        @SneakyThrows
        public ChargeHttpInputMessage(HttpInputMessage inputMessage, Class<?> type) {
            this.headers = inputMessage.getHeaders();
            byte[] body = ByteStreams.toByteArray(inputMessage.getBody());
            log.info("receive: \n{}", new String(body, "UTF-8"));
            Request<String> request = objectMapper.readValue(body, new TypeReference<Request<String>>() {});

            log.info("sign: {}", request.checkSign(chargeNotifyProperties.getSigSecret()));
//            if(!request.checkSign(chargingNotifyProperties.getSigSecret())){
//                throw new SignVerifyFailureException(new String(body, "UTF-8"));
//            }

            Request<Object> newRequest = request.decrypt(
                chargeNotifyProperties.getDataSecretIV(),
                chargeNotifyProperties.getDataSecret(),
                s -> objectMapper.readValue(s, type)
            );
            body = objectMapper.writeValueAsBytes(newRequest);
            this.body = ByteSource.wrap(body).openStream();
        }

        @Override
        public InputStream getBody() {
            return body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }
    }
}
